package com.ruoyi.framework.security.jsonlogin;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.CaptchaExpireException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.SysLoginService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 *
 */
@Setter
@Slf4j
public class JsonLoginUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    // 设置拦截/sms/login短信登录接口
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/json/login", "POST");

    private SysLoginService sysLoginService;

    public JsonLoginUsernamePasswordAuthenticationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String body = ServletUtil.getBody(request);
        if (StringUtils.isBlank(body)) {
            throw new AuthenticationServiceException("请输入用户名和密码!");
        }
        LoginBody loginBody = JSON.parseObject(body, LoginBody.class);
        preAuthenticationChecks(loginBody);
        JsonLoginAuthenticationToken jsonLoginAuthenticationToken = new JsonLoginAuthenticationToken(loginBody.getUsername(), loginBody.getPassword(), loginBody);
        setDetails(request, jsonLoginAuthenticationToken);
        return this.getAuthenticationManager().authenticate(jsonLoginAuthenticationToken);
    }

    /**
     * 提供，以便子类可以配置放入身份验证请求的详细信息属性的内容。
     *
     * @param request                      请求
     * @param jsonLoginAuthenticationToken 凭证
     */

    private void setDetails(HttpServletRequest request, JsonLoginAuthenticationToken jsonLoginAuthenticationToken) {
        jsonLoginAuthenticationToken.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    /**
     * 前置检查校验
     * 例如 验证码校验
     *
     * @param loginBody 登录信息
     */
    private void preAuthenticationChecks(LoginBody loginBody) {
        String username = loginBody.getUsername();
        String password = loginBody.getPassword();
        // 用户名或密码为空 错误
        if (StringUtils.isAnyBlank(username, password)) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UsernameNotFoundException("请输入用户名和密码!");
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new BadCredentialsException("密码不符合规则!");
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new BadCredentialsException("请输入正确的用户名!");
        }
        try {
            sysLoginService.validateCaptcha(loginBody.getUsername(), loginBody.getCode(), loginBody.getUuid());
        } catch (CaptchaExpireException e) {
            throw new BadCredentialsException("验证码已过期");
        } catch (CaptchaException e) {
            throw new BadCredentialsException("验证码不正确");
        }
        sysLoginService.ipBlacklistVerification(username);
    }
}
